package main

import (
	"io"
	"log"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"time"

	"gitee.com/kitfast/resml"
)

type printableReader struct {
	r io.Reader
}

func (pr *printableReader) Read(p []byte) (n int, err error) {
	n, err = pr.r.Read(p)
	log.Printf("Printable Reader: %d %v", n, err)
	return
}

func main() {
	root := "/tmp/resource-manager-url-client"
	if os.PathSeparator == '\\' {
		root = "D:\\tmp\\resource-manager-url-client"
	}
	var fn string
	argl := len(os.Args)
	if argl < 3 {
		log.Fatal("	usage: url-client <method> <url> [<filepath> [<cache-root>]]")
	}
	if argl > 4 {
		root = os.Args[4]
	}
	if argl > 3 {
		fn = os.Args[3]
	}
	cache, err := resml.NewOsFileObjectStorage(root, true, nil)
	if err != nil {
		log.Fatal(err)
	}
	uc := resml.NewUrlClient(cache)
	uri, err := url.Parse(os.Args[2])
	if err != nil {
		log.Fatal(err)
	}
	switch strings.ToLower(os.Args[1]) {
	case "get":
		get(uc, uri, fn)
	case "head", "meta":
		head(uc, uri)
	case "put":
		put(uc, uri, fn)
	case "del", "remove", "delete":
		del(uc, uri)
	}
}

func head(uc *resml.UrlClient, uri *url.URL) {
	res := uc.Get(uri)
	if res == nil {
		log.Fatalf("not suppported resource: %s", uri)
	}
	m, err := res.Meta()
	if err != nil {
		log.Fatal(err)
	}
	os.Stdout.WriteString(resml.MetaHttpHeaderText(m))
}

func get(uc *resml.UrlClient, uri *url.URL, fn string) {
	res := uc.Get(uri)
	if res == nil {
		log.Fatalf("not suppported resource: %s", uri)
	}
	// 进度条的需要在 res.Get 之前启动。
	// 进度条是从远程下载时才会出现，如果本地已经有缓存，进度条不会设置
	go func() {
		pg, ok := res.(resml.Progress)
		if !ok {
			if gp, ok := res.(resml.GetProgress); ok {
				pg = gp.GetProgress()
			}
		}
		if pg == nil {
			log.Print("progress not supported")
			return
		}
		to := time.Second * 10
		ts := time.Now()
		tm := time.NewTicker(time.Millisecond * 100)
		defer tm.Stop()
		for range tm.C {
			log.Printf("Progress: %.2f%%, %d/%d", pg.Percent(), pg.Current(), pg.Total())
			if total := pg.Total(); total > 0 && pg.Current() >= total {
				// total 大于0表示进度条已经设置
				break
			} else if total <= 0 && time.Since(ts) > to {
				break // 超时
			}
		}
	}()
	out := os.Stdout
	if fn != "" {
		if fd, err := create(fn); err == nil {
			defer fd.Close()
			out = fd
		}
	}
	r, m, err := res.Get()
	if err != nil {
		log.Fatal(err)
	}
	defer r.Close()
	os.Stdout.WriteString(resml.MetaHttpHeaderText(m))
	os.Stdout.WriteString("\r\n")
	io.Copy(out, &printableReader{r: r})
	log.Printf("GET %s OK, file: %s", res.Uri(), fn)
}

func put(uc *resml.UrlClient, uri *url.URL, fn string) {
	if fn == "" {
		log.Fatal("file name is empty")
	}
	meta, err := resml.MetaOfFile(fn, resml.NewSha256HexEtager())
	if err != nil {
		log.Fatal(err)
	}
	res := uc.GetWritableWithMeta(uri, meta)
	if res == nil {
		log.Fatalf("not suppported resource: %s", uri)
	}
	log.Println(resml.MetaHttpHeaderText(meta))
	fd, err := os.Open(fn)
	if err != nil {
		log.Fatal(err)
	}
	defer fd.Close()
	if strings.HasPrefix(uri.Path, "/logs/") { // 以追加的方式写入日志类的资源
		// rmeta 用于定位，如果本地有偏移，则无法请求 meta。
		rmeta, err := res.Meta()
		if err != nil {
			log.Fatal(err)
		}
		// Server 需要实现以 /logs/ 开头的资源以追加方式写入。
		if _, err = fd.Seek(rmeta.ContentLength(), io.SeekStart); err != nil {
			log.Fatal(err)
		}
	}
	if err = res.Put(meta, fd); err != nil {
		log.Fatal(err)
	}
	log.Printf("PUT %s OK, file: %s", res.Uri(), fn)
}

func del(uc *resml.UrlClient, uri *url.URL) {
	res := uc.GetWritableWithMeta(uri, nil)
	if res == nil {
		log.Fatalf("not suppported resource: %s", uri)
	}
	if err := res.Remove(); err != nil {
		log.Fatal(err)
	}
	log.Printf("DEL %s OK", res.Uri())
}

func create(fn string) (*os.File, error) {
	if err := os.MkdirAll(filepath.Dir(fn), 0755); err != nil {
		return nil, err
	}
	return os.OpenFile(fn, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
}
